const alicloud = require("@alicloud/fc20230330");
const readline = require("readline");
const { exec } = require("child_process");
const util = require("util");
const JSZip = require("jszip");
const fs = require("fs");
const path = require("path");

class Client {
  constructor() {
    this.endpoint = "";
  }
  /**
   * 使用AK&SK初始化账号Client
   * @return Client
   * @throws Exception
   */
  _createClient() {
    const aliyun = process.env["ALIYUN"];
    if (!aliyun) {
      throw new Error("AK&SK不存在，请设置环境变量配置ALIYUN，格式为：AK,SK");
    }
    if (this.endpoint === "") {
      throw new Error("endpoint不存在，构造传入");
    }
    return new alicloud.default({
      accessKeyId: aliyun.split(",")[0],
      accessKeySecret: aliyun.split(",")[1],
      endpoint: this.endpoint,
      regionId: this.endpoint.split(".")[1],
      readTimeout: 30000,
      connectTimeout: 30000,
    });
  }

  _question(rl, query) {
    return new Promise((resolve) => rl.question(query, resolve));
  }

  // 递归读取目录内容并添加到 zip 对象
  _addFolderToZip(zip, folderPath) {
    const files = fs.readdirSync(folderPath);

    files.forEach((fileName) => {
      const filePath = path.join(folderPath, fileName);
      const stat = fs.statSync(filePath);

      if (stat.isFile()) {
        const fileData = fs.readFileSync(filePath);
        zip.file(fileName, fileData);
      } else if (stat.isDirectory()) {
        this._addFolderToZip(zip.folder(fileName), filePath);
      }
    });
  }

  async _getSubfix(client, project) {
    // 先通过检测实际项目名称
    const tryName = ["", "$default3", "$default2", "$default"];
    let subfix = "";
    for (let nName of tryName) {
      try {
        const functionInfo = await client.getFunction(
          project + "-prod" + nName,
          new alicloud.GetFunctionRequest({})
        );
        subfix = nName;
        break;
      } catch (e) {}
    }
    console.log("实际项目名称：", project + "-prod" + subfix);
    return subfix;
  }

  async publish(project, endpoint) {
    this.endpoint = endpoint;
    let hasTest = true;
    let hasStage = true;

    const client = this._createClient();
    const subfix = await this._getSubfix(client, project);

    let versions;
    versions = (
      await client.listFunctionVersions(
        project + "-prod" + subfix,
        new alicloud.ListFunctionVersionsRequest({})
      )
    ).body.versions;
    // 检查test
    try {
      const functionInfo = await client.getFunction(
        project + "-test" + subfix,
        new alicloud.GetFunctionRequest({})
      );
      hasTest = true;
    } catch (e) {
      hasTest = false;
    }
    // 检查stage
    try {
      const functionInfo = await client.getFunction(
        project + "-stage" + subfix,
        new alicloud.GetFunctionRequest({})
      );
      hasStage = true;
    } catch (e) {
      hasStage = false;
    }

    console.log(
      "上一个版本：",
      versions.length == 0 ? "无" : versions[0].description
    );
    // 判断上个版本是否存在，存在并且格式类似1.0.0，最后一位加一
    let lastVersion =
      versions.length == 0
        ? "1.0.0.-1"
        : versions[0].description.match(/[\d.]*/)[0];
    if (lastVersion == "") lastVersion = "1.0.0.-1";
    let defaultVersion = lastVersion.split(".");
    // 进一
    defaultVersion[defaultVersion.length - 1] =
      parseInt(defaultVersion[defaultVersion.length - 1]) + 1;
    // 用户输入版本
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
    });
    let targetVersion = await this._question(
      rl,
      `请输入发布版本（回车为${defaultVersion.join(".")}）：`
    );
    targetVersion =
      targetVersion == "" ? defaultVersion.join(".") : targetVersion;
    rl.close();
    // 打包
    console.log("###### Git");
    try {
      const git = await util.promisify(exec)("git pull");
      console.log(git.stdout);
    } catch (e) {
      console.log(e.message);
    }
    console.log("###### Build");
    try {
      fs.rmSync(path.join(process.cwd(), "dist"), { recursive: true });
    } catch (e) {}

    let packName = "npm";
    try {
      await util.promisify(exec)("pnpm -v", { cwd: process.cwd() });
      packName = "pnpm";
    } catch (e) {
      console.log("pnpm not found, use npm");
    }
    const npmUpdate = await util.promisify(exec)(packName + " i");
    console.log(npmUpdate.stdout);
    const build = await util.promisify(exec)(packName + " run build");
    console.log(build.stdout);

    // 执行额外的pre upload构建命令
    try {
      console.log("###### Pre Upload (optional)");
      await util.promisify(exec)("sh pre-upload.sh", { cwd: process.cwd() });
    } catch (e) {
      console.log(e.message);
    }

    // zip代码dist中的文件
    console.log("###### Zip dist folder");
    const zip = new JSZip();
    const distPath = path.join(process.cwd(), "dist");
    this._addFolderToZip(zip, distPath);
    const zipData = await zip.generateAsync({
      type: "base64",
    });

    console.log("###### Upload");
    const uploadData = {
      code: {
        zipFile: zipData,
      },
    };
    if (hasTest) {
      await client.updateFunction(
        project + "-test" + subfix,
        new alicloud.UpdateFunctionRequest({
          body: new alicloud.UpdateFunctionInput(uploadData),
        })
      );
    }
    if (hasStage) {
      await client.updateFunction(
        project + "-stage" + subfix,
        new alicloud.UpdateFunctionRequest({
          body: new alicloud.UpdateFunctionInput(uploadData),
        })
      );
    }
    await client.updateFunction(
      project + "-prod" + subfix,
      new alicloud.UpdateFunctionRequest({
        body: new alicloud.UpdateFunctionInput(uploadData),
      })
    );
    // 打版本
    console.log("###### Version");
    const versionData = { description: targetVersion };
    let versionTest;
    if (hasTest) {
      versionTest = (
        await client.publishFunctionVersion(
          project + "-test" + subfix,
          new alicloud.PublishFunctionVersionRequest({
            body: new alicloud.PublishVersionInput(versionData),
          })
        )
      ).body;
    }
    if (hasStage) {
      await client.publishFunctionVersion(
        project + "-stage" + subfix,
        new alicloud.PublishFunctionVersionRequest({
          body: new alicloud.PublishVersionInput(versionData),
        })
      );
    }
    await client.publishFunctionVersion(
      project + "-prod" + subfix,
      new alicloud.PublishFunctionVersionRequest({
        body: new alicloud.PublishVersionInput(versionData),
      })
    );
    // 测试的自动发布最新版本
    if (hasTest) {
      await client.updateAlias(
        project + "-test" + subfix,
        "test",
        new alicloud.UpdateAliasRequest({
          body: new alicloud.UpdateAliasInput({
            versionId: versionTest.versionId,
          }),
        })
      );
    }
  }

  async toLatest(project, endpoint, flag) {
    this.endpoint = endpoint;
    const client = this._createClient();
    const subfix = await this._getSubfix(client, project);

    let versions;
    versions = (
      await client.listFunctionVersions(
        project + "-" + flag + subfix,
        new alicloud.ListFunctionVersionsRequest({})
      )
    ).body.versions;
    if (versions.length == 0) {
      console.log("没有版本");
      return;
    }

    await client.updateAlias(
      project + "-prod" + subfix,
      flag,
      new alicloud.UpdateAliasRequest({
        body: new alicloud.UpdateAliasInput({
          versionId: versions[0].versionId,
        }),
      })
    );

    console.log("change to", versions[0].description);
  }
}

module.exports = Client;
